package org.springframework.util;

import java.io.File;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * @author Garrett.Xia
 * @date 2021/11/5 0005 - 10:19
 */
public class ClassUtils {

    /**
     * 从package（包）中获取所有的Class
     * @param packageName 包名
     * @return 类文件
     */
    public static List<Class<?>> getClasses(String packageName){

        List<Class<?>> classes = new ArrayList<>();
        // 开启循环迭代
        boolean recursive = true;
        // 获取包的名字，并进行替换
        String packageDirName = packageName.replace('.','/');

        Enumeration<URL> dirs;
        try{
            dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
            while (dirs.hasMoreElements()) {
                URL url = dirs.nextElement();
                String protocol = url.getProtocol();
                // 如果是以文件的形式保存在服务器上
                if ("file".equals(protocol)){
                    /* 获取包的物理路径 */
                    String filePathname = URLDecoder.decode(url.getFile(), "UTF-8");
                    /* 以文件的方式扫描整个包下的文件， 并添加到集合中 */
                    findAndAddClassesInPackageByFile(packageName, filePathname,  recursive, classes);
                }else if("jar".equals(protocol)){
                    JarFile jar;
                    try {
                        jar = ((JarURLConnection)url.openConnection()).getJarFile();
                        Enumeration<JarEntry> entries = jar.entries();
                        while (entries.hasMoreElements()){
                            JarEntry entry = entries.nextElement();
                            String name = entry.getName();
                            if (name.charAt(0) == '/') {
                                // 如果是以/开头的,获取后面的字符串
                                name = name.substring(1);
                            }
                            if (name.startsWith(packageDirName)) {
                                int idx = name.lastIndexOf('/');
                                if (idx != -1) {
                                    // 获取包名 把"/"替换成"."
                                    packageName = name.substring(0,idx).replace('/','.');
                                }
                                if ((idx != -1 || recursive)) {
                                    if (name.endsWith(".class") && !entry.isDirectory()) {
                                        String className = name.substring(packageName.length() + 1, name.length() - 6);
                                        try {
                                            classes.add(Class.forName(packageName + '.' + className));
                                        } catch (ClassNotFoundException e){
                                            e.printStackTrace();
                                        }
                                    }
                                }

                            }
                        }
                    }catch(IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }

        return classes;
    }

    /**
     * 以文件形式来获取包下的所有Class
     * @param packageName 包名
     * @param packagePath 包路径
     * @param recursive 循环迭代 true-开启 false-关闭
     * @param classes 类集合
     */
    public static void findAndAddClassesInPackageByFile(String packageName, String packagePath, final boolean recursive,
                                                        List<Class<?>> classes) {
        File dir = new File(packagePath);
        /* 如果不存在 或者 不是目录 就直接返回*/
        if (!dir.exists() || !dir.isDirectory()) {
            return;
        }
        /* 如果存在 就获取包下的所有文件 包括目录 */
        File[] dirfiles = dir.listFiles(file -> {
            // 自定义过滤规则：如果可以循环（包含子目录）或以.class结尾的文件
            return (recursive && file.isDirectory() || (file.getName().endsWith(".class")));
        });

        // 循环所有文件
        for (File file : dirfiles) {
            if (file.isDirectory()){
                findAndAddClassesInPackageByFile(packageName + "." + file.getName(),file.getAbsolutePath(),
                        recursive, classes);
            }else {
                // 如果是java类文件，则去除后面的.class，只留下类名
                String className = file.getName().substring(0,file.getName().length() - 6);
                try {
                    classes.add(Class.forName(packageName + '.' + className));
                }catch (ClassNotFoundException e){
                    e.printStackTrace();
                }
            }
        }


    }


}
